const STATE = {
  INITAL: 'INITAL',
  CONNECTING: 'CONNECTING',
  OPENED: 'OPENED',
  ERROR: 'ERROR',
  CLOSING: 'CLOSING',
  CLOSED: 'CLOSED'
}

export class WebSocketClient {
  instance = null
  state = STATE.INITAL
  checkerTimer = null
  checkerInterval = 5 * 1000
  lastReceived = null

  constructor(url, options = {}) {
    this.url = url
    this.reconnectDelay = options.reconnectDelay || 1000
    this.heartbeatInterval = options.heartbeatInterval || 1000 * 30 + (1000 * 10)
    this.heartbeatMsg = options.heartbeatMsg || 'ping'
  }

  connect() {
    if (typeof WebSocket !== 'function') throw new Error('浏览器不支持 WebSocket')

    if ([STATE.CONNECTING, STATE.OPENED].includes(this.state)) return

    this.instance = new WebSocket(this.url)

    this.state = STATE.CONNECTING

    this.instance.onopen = () => {
      this.lastReceived = new Date().getTime()
      this.opened()
    }

    this.instance.onmessage = (event) => {
      this.lastReceived = new Date().getTime()
      const data = JSON.parse(event.data)
      this.onMessage(data)
    }

    this.instance.onerror = (event) => {
      this.state = STATE.ERROR
      this.onError(event)
    }

    this.instance.onclose = (e) => {
      this.state = STATE.CLOSED

      if (e.code !== 1000) {
        this.reconnect()
      }
    }
  }

  opened() {

  }

  onMessage(data) {

  }

  onError(event) {

  }

  reconnect() {
    if ([STATE.INITAL, STATE.ERROR, STATE.CLOSED].includes(this.state)) {
      setTimeout(() => {
        this.connect()
      }, this.reconnectDelay)
    }
  }

  wsIsInlineChecker() {
    this.stopWsChecker()
    this.checkerTimer = setInterval(() => {
      const sinceLastReceived = new Date().getTime() - (this.lastReceived || 0)
      if (
        this.instance === null ||
        [STATE.INITAL, STATE.ERROR, STATE.CLOSED].includes(this.state) ||
        (this.lastReceived !== null && sinceLastReceived > this.heartbeatInterval)
      ) {
        this.reconnect()
      }
    }, this.checkerInterval)
  }

  stopWsChecker() {
    if (typeof this.checkerTimer !== 'number' || isNaN(this.checkerTimer)) { return }

    clearInterval(this.checkerTimer)
  }

  close() {
    this.state = STATE.CLOSING
    this.instance.close()
  }
}
